#!/bin/groovy
/*
 * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The OpenAirInterface Software Alliance licenses this file to You under
 * the OAI Public License, Version 1.1  (the "License"); you may not use this file
 * except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.openairinterface.org/?page_id=698
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *-------------------------------------------------------------------------------
 * For more information about the OpenAirInterface (OAI) Software Alliance:
 *      contact@openairinterface.org
 */

// Location of the CN executor node
def cn_ci_host = params.Host_CN_CI_Server
def cn_ci_resource = params.DockerContainers
def upstreamEvent = false

// Variables that can used in main pipeline and in functions
NRF=0
AMF=1
SMF=2
UPF=3
AUSF=4
UDM=5
UDR=6
NSSF=7
UPF_VPP=8
PCF=9

// Default tags / branches  --> could be passed on by upstream job
imageNames = ["oai-nrf", "oai-amf", "oai-smf", "oai-upf", "oai-ausf", "oai-udm", "oai-udr", "oai-nssf", "oai-upf-vpp", "oai-pcf"]
repoNames = ["oai-cn5g-nrf", "oai-cn5g-amf", "oai-cn5g-smf", "oai-cn5g-upf", "oai-cn5g-ausf", "oai-cn5g-udm", "oai-cn5g-udr", "oai-cn5g-nssf", "oai-cn5g-upf-vpp", "oai-cn5g-pcf"]
branchNames = ["", "", "", "", "", "", "", "", "", ""]
imageTags = ["", "", "", "", "", "", "", "", "", ""]
upstreamJobs = [false, false, false, false, false, false, false, false, false, false]
pulledImages = [false, false, false, false, false, false, false, false, false, false]

// Which RAN TAG to use
ranTag = params.ranTag
upstreamTagToUse = params.upstreamTagToUse

// means to shorten the pipeline
run_mini_gnbsim = true
run_static_ue_ip = true
run_vpp_upf = true
run_slicing_w_nssf = true
run_ulcl_pcf = true
run_mongo_db = true

//-------------------------------------------------------------------------------
// Pipeline start
pipeline {
  agent {
    label cn_ci_host
  }
  options {
    disableConcurrentBuilds()
    timestamps()
    ansiColor('xterm')
    lock(cn_ci_resource)
  }
  stages {
    stage ('Verify Parameters') {
      steps {
        script {
          echo '\u2705 \u001B[32mVerify Parameters\u001B[0m'

          JOB_TIMESTAMP = sh returnStdout: true, script: 'date --utc --rfc-3339=seconds | sed -e "s#+00:00##"'
          JOB_TIMESTAMP = JOB_TIMESTAMP.trim()

          echo "Node       is ${NODE_NAME}"

          // Find out the cause of the trigger
          for (cause in currentBuild.getBuildCauses()) {
            if (cause.toString().contains('UpstreamCause')) {
              upstreamEvent = true
            }
            if (cause.toString().contains('OAI-CN5G-NRF')) {
              upstreamJobs[NRF] = true
            }
            if (cause.toString().contains('OAI-CN5G-AMF')) {
              upstreamJobs[AMF] = true
            }
            if (cause.toString().contains('OAI-CN5G-SMF')) {
              upstreamJobs[SMF] = true
            }
            if (cause.toString().contains('OAI-CN5G-UPF')) {
              upstreamJobs[UPF] = true
            }
            if (cause.toString().contains('OAI-CN5G-AUSF')) {
              upstreamJobs[AUSF] = true
            }
            if (cause.toString().contains('OAI-CN5G-UDM')) {
              upstreamJobs[UDM] = true
            }
            if (cause.toString().contains('OAI-CN5G-UDR')) {
              upstreamJobs[UDR] = true
            }
            if (cause.toString().contains('OAI-CN5G-NSSF')) {
              upstreamJobs[NSSF] = true
            }
            if (cause.toString().contains('OAI-CN5G-UPF-VPP')) {
              upstreamJobs[UPF_VPP] = true
            }
            if (cause.toString().contains('OAI-CN5G-PCF')) {
              upstreamJobs[PCF] = true
            }
          }
          sh "git clean -x -d -f > /dev/null 2>&1"
          sh "git submodule foreach --recursive 'git clean -x -d -ff' > /dev/null 2>&1"
          sh "git submodule deinit --force --all > /dev/null 2>&1"
          // For any upstream job (PR or post-merge), let run on certified tag.
          if (upstreamEvent) {
            sh 'git checkout -f ' + upstreamTagToUse
          }
          sh 'git submodule update --init --recursive ci-scripts/common'
          sh "mkdir -p archives"

          for (ii = 0; ii < imageNames.size(); ii++) {
            if (ii == NRF) {
              branchNames[NRF] = params.NRF_BRANCH
              imageTags[NRF] = params.NRF_TAG
              if (upstreamEvent && upstreamJobs[NRF]) {
                echo "Upstream Job passed NRF_TAG to use: ${imageTags[NRF]}"
                echo "Upstream Job passed NRF_BRANCH to use: ${branchNames[NRF]}"
                run_mini_gnbsim = false
                run_static_ue_ip = true
                run_vpp_upf = true
                run_slicing_w_nssf = true
                run_ulcl_pcf = false
                run_mongo_db = false
              }
            }
            if (ii == AMF) {
              branchNames[AMF] = params.AMF_BRANCH
              imageTags[AMF] = params.AMF_TAG
              if (upstreamEvent && upstreamJobs[AMF]) {
                echo "Upstream Job passed AMF_TAG to use: ${imageTags[AMF]}"
                echo "Upstream Job passed AMF_BRANCH to use: ${branchNames[AMF]}"
                run_mini_gnbsim = true
                run_static_ue_ip = true
                run_vpp_upf = true
                run_slicing_w_nssf = true
                run_ulcl_pcf = true
                run_mongo_db = false
              }
            }
            if (ii == SMF) {
              branchNames[SMF] = params.SMF_BRANCH
              imageTags[SMF] = params.SMF_TAG
              if (upstreamEvent && upstreamJobs[SMF]) {
                echo "Upstream Job passed SMF_TAG to use: ${imageTags[SMF]}"
                echo "Upstream Job passed SMF_BRANCH to use: ${branchNames[SMF]}"
                run_mini_gnbsim = true
                run_static_ue_ip = true
                run_vpp_upf = true
                run_slicing_w_nssf = true
                run_ulcl_pcf = true
                run_mongo_db = false
              }
            }
            if (ii == UPF) {
              branchNames[UPF] = params.UPF_BRANCH
              imageTags[UPF] = params.UPF_TAG
              if (upstreamEvent && upstreamJobs[UPF]) {
                echo "Upstream Job passed UPF_TAG to use: ${imageTags[UPF]}"
                echo "Upstream Job passed UPF_BRANCH to use: ${branchNames[UPF]}"
                run_mini_gnbsim = true
                run_static_ue_ip = true
                run_vpp_upf = false
                run_slicing_w_nssf = true
                run_ulcl_pcf = false
                run_mongo_db = false
              }
            }
            if (ii == AUSF) {
              branchNames[AUSF] = params.AUSF_BRANCH
              imageTags[AUSF] = params.AUSF_TAG
              if (upstreamEvent && upstreamJobs[AUSF]) {
                echo "Upstream Job passed AUSF_TAG to use: ${imageTags[AUSF]}"
                echo "Upstream Job passed AUSF_BRANCH to use: ${branchNames[AUSF]}"
                run_mini_gnbsim = false
                run_static_ue_ip = true
                run_vpp_upf = true
                run_slicing_w_nssf = true
                run_ulcl_pcf = false
                run_mongo_db = false
              }
            }
            if (ii == UDM) {
              branchNames[UDM] = params.UDM_BRANCH
              imageTags[UDM] = params.UDM_TAG
              if (upstreamEvent && upstreamJobs[UDM]) {
                echo "Upstream Job passed UDM_TAG to use: ${imageTags[UDM]}"
                echo "Upstream Job passed UDM_BRANCH to use: ${branchNames[UDM]}"
                run_mini_gnbsim = false
                run_static_ue_ip = true
                run_vpp_upf = true
                run_slicing_w_nssf = true
                run_ulcl_pcf = false
                run_mongo_db = false
              }
            }
            if (ii == UDR) {
              branchNames[UDR] = params.UDR_BRANCH
              imageTags[UDR] = params.UDR_TAG
              if (upstreamEvent && upstreamJobs[UDR]) {
                echo "Upstream Job passed UDR_TAG to use: ${imageTags[UDR]}"
                echo "Upstream Job passed UDR_BRANCH to use: ${branchNames[UDR]}"
                run_mini_gnbsim = false
                run_static_ue_ip = true
                run_vpp_upf = true
                run_slicing_w_nssf = true
                run_ulcl_pcf = false
                run_mongo_db = true
              }
            }
            if (ii == NSSF) {
              branchNames[NSSF] = params.NSSF_BRANCH
              imageTags[NSSF] = params.NSSF_TAG
              if (upstreamEvent && upstreamJobs[NSSF]) {
                echo "Upstream Job passed NSSF_TAG to use: ${imageTags[NSSF]}"
                echo "Upstream Job passed NSSF_BRANCH to use: ${branchNames[NSSF]}"
                run_mini_gnbsim = false
                run_static_ue_ip = false
                run_vpp_upf = false
                run_slicing_w_nssf = true
                run_ulcl_pcf = false
                run_mongo_db = false
              }
            }
            if (ii == UPF_VPP) {
              branchNames[UPF_VPP] = params.UPF_VPP_BRANCH
              imageTags[UPF_VPP] = params.UPF_VPP_TAG
              if (upstreamEvent && upstreamJobs[UPF_VPP]) {
                echo "Upstream Job passed UPF_VPP_TAG to use: ${imageTags[UPF_VPP]}"
                echo "Upstream Job passed UPF_VPP_BRANCH to use: ${branchNames[UPF_VPP]}"
                run_mini_gnbsim = false
                run_static_ue_ip = false
                run_vpp_upf = true
                run_slicing_w_nssf = true
                run_ulcl_pcf = true
                run_mongo_db = false
              }
            }
            if (ii == PCF) {
              branchNames[PCF] = params.PCF_BRANCH
              imageTags[PCF] = params.PCF_TAG
              if (upstreamEvent && upstreamJobs[PCF]) {
                echo "Upstream Job passed PCF_TAG to use: ${imageTags[PCF]}"
                echo "Upstream Job passed PCF_BRANCH to use: ${branchNames[PCF]}"
                run_mini_gnbsim = false
                run_static_ue_ip = false
                run_vpp_upf = false
                run_slicing_w_nssf = false
                run_ulcl_pcf = true
                run_mongo_db = false
              }
            }
          }


          // On Ubuntu servers, we shall pull from private local registry
          if ((cn_ci_host == 'Selfix') || (cn_ci_host == 'Alambix') || (cn_ci_host == 'Cetautomatix')) {
            pullFromSelfix = true
            try {
              // Login
              sh 'docker login -u oaicicd -p oaicicd selfix.sboai.cs.eurecom.fr > /dev/null 2>&1'
            } catch (Exception e) {
              echo 'Problem w/ selfix registry. Let see if we can use local images'
              pullFromSelfix = false
            }
            if (pullFromSelfix) {
              // We will try to pull from Selfix registry.
              // If the NF CI is yet ready, it's OK, it will fail but keep working with local registry
              for (ii = 0; ii < imageNames.size(); ii++) {
                imageTags[ii] = pullImageFromSelfix(ii, imageNames[ii], imageTags[ii], branchNames[ii])
              }
              // Logout
              sh 'docker logout elfix.sboai.cs.eurecom.fr > /dev/null 2>&1'
            }
          }

          // Verify that the images are available
          imageStatus = 0
          for (ii = 0; ii < imageNames.size(); ii++) {
            imageStatus += checkImageInfo(imageNames[ii], imageTags[ii])
          }
          if (imageStatus > 0) {
            error ("Some images are not present!")
          }
        }
      }
    }
    stage ('Check Minimalist Deployment Tutorial') {
      when { expression {run_mini_gnbsim} }
      steps {
        script {
          updateDockerCompose('docker-compose/docker-compose-mini-nonrf.yaml', ranTag)
          dir ('ci-scripts') {
            // Tee will make the command always pass
            // Please use the same log name as the folder used in the tutorial
            sh './checkTutorial.py --tutorial DEPLOY_SA5G_MINI_WITH_GNBSIM.md | tee ../archives/mini-gnbsim.log'
          }
          try {
            sh 'grep -l "is PASS" archives/mini-gnbsim.log > /dev/null'
            echo "Minimalist gnbsim Tutorial PASSED"
          } catch (Exception e) {
            currentBuild.result = 'FAILURE'
            echo "Minimalist gnbsim Tutorial FAILED"
          }
          archivesArtifacts('docker-compose/docker-compose-mini-nonrf.yaml', 'mini-gnbsim')
          cleanUpDockerCompose('docker-compose/docker-compose-mini-nonrf.yaml')
        }
      }
    }
    stage ('Check Static UE IP Tutorial') {
      when { expression {run_static_ue_ip} }
      steps {
        script {
          updateDockerCompose('docker-compose/docker-compose-basic-nrf.yaml', ranTag)
          dir ('ci-scripts') {
            // Tee will make the command always pass
            // Please use the same log name as the folder used in the tutorial
            sh './checkTutorial.py --tutorial DEPLOY_SA5G_BASIC_STATIC_UE_IP.md | tee ../archives/static-ue-ip.log'
          }
          try {
            sh 'grep -l "is PASS" archives/static-ue-ip.log > /dev/null'
            echo "Static UE IP Tutorial PASSED"
          } catch (Exception e) {
            currentBuild.result = 'FAILURE'
            echo "Static UE IP Tutorial FAILED"
          }
          archivesArtifacts('docker-compose/docker-compose-basic-nrf.yaml', 'static-ue-ip')
          cleanUpDockerCompose('docker-compose/docker-compose-basic-nrf.yaml')
        }
      }
    }
    stage ('Check UPF-VPP Tutorial') {
      when { expression {run_vpp_upf} }
      steps {
        script {
          updateDockerCompose('docker-compose/docker-compose-basic-vpp-nrf.yaml', ranTag)
          dir ('ci-scripts') {
            // Tee will make the command always pass
            // Please use the same log name as the folder used in the tutorial
            sh './checkTutorial.py --tutorial DEPLOY_SA5G_WITH_VPP_UPF.md | tee ../archives/vpp-upf-gnbsim.log'
          }
          try {
            sh 'grep -l "is PASS" archives/vpp-upf-gnbsim.log > /dev/null'
            echo "UPF-VPP Tutorial PASSED"
          } catch (Exception e) {
            currentBuild.result = 'FAILURE'
            echo "UPF-VPP Tutorial FAILED"
          }
          archivesArtifacts('docker-compose/docker-compose-basic-vpp-nrf.yaml', 'vpp-upf-gnbsim')
          cleanUpDockerCompose('docker-compose/docker-compose-basic-vpp-nrf.yaml')
        }
      }
    }
    stage ('Check NSSF-Slicing Tutorial') {
      when { expression {run_slicing_w_nssf} }
      steps {
        script {
          updateDockerCompose('docker-compose/docker-compose-slicing-basic-nrf.yaml', ranTag)
          dir ('ci-scripts') {
            // Tee will make the command always pass
            // Please use the same log name as the folder used in the tutorial
            sh './checkTutorial.py --tutorial DEPLOY_SA5G_SLICING.md | tee ../archives/slicing-with-nssf.log'
          }
          try {
            sh 'grep -l "is PASS" archives/slicing-with-nssf.log > /dev/null'
            echo "NSSF-Slicing Tutorial PASSED"
          } catch (Exception e) {
            currentBuild.result = 'FAILURE'
            echo "NSSF-Slicing Tutorial FAILED"
          }
          archivesArtifacts('docker-compose/docker-compose-slicing-basic-nrf.yaml', 'slicing-with-nssf')
          cleanUpDockerCompose('docker-compose/docker-compose-slicing-basic-nrf.yaml')
        }
      }
    }
    stage ('Check UL-CL Policies Tutorial') {
      when { expression {run_ulcl_pcf} }
      steps {
        script {
          updateDockerCompose('docker-compose/docker-compose-basic-vpp-pcf-ulcl.yaml', ranTag)
          dir ('ci-scripts') {
            // Tee will make the command always pass
            // Please use the same log name as the folder used in the tutorial
            sh './checkTutorial.py --tutorial DEPLOY_SA5G_ULCL.md | tee ../archives/ulcl-scenario.log'
          }
          try {
            sh 'grep -l "is PASS" archives/ulcl-scenario.log > /dev/null'
            echo "UL-CL-Policies Tutorial PASSED"
          } catch (Exception e) {
            currentBuild.result = 'FAILURE'
            echo "UL-CL-Policies Tutorial FAILED"
          }
          archivesArtifacts('docker-compose/docker-compose-basic-vpp-pcf-ulcl.yaml', 'ulcl-scenario')
          cleanUpDockerCompose('docker-compose/docker-compose-basic-vpp-pcf-ulcl.yaml')
        }
      }
    }
    stage ('Check MongoDB deployment Tutorial') {
      when { expression {run_mongo_db} }
      steps {
        script {
          updateDockerCompose('docker-compose/docker-compose-basic-nrf-mongodb.yaml', ranTag)
          dir ('ci-scripts') {
            // Tee will make the command always pass
            // Please use the same log name as the folder used in the tutorial
            sh './checkTutorial.py --tutorial DEPLOY_SA5G_BASIC_MONGODB.md | tee ../archives/mongodb-test.log'
          }
          try {
            sh 'grep -l "is PASS" archives/mongodb-test.log > /dev/null'
            echo "MongoDB Deploy Tutorial PASSED"
          } catch (Exception e) {
            currentBuild.result = 'FAILURE'
            echo "MongoDB Deploy Tutorial FAILED"
          }
          archivesArtifacts('docker-compose/docker-compose-basic-nrf-mongodb.yaml', 'mongodb-test')
          cleanUpDockerCompose('docker-compose/docker-compose-basic-nrf-mongodb.yaml')
        }
      }
    }
  }
  post {
    always {
      script {
        // Zipping all archived log files
        sh "zip -r -qq cn5g_fed_tutorials_logs.zip archives"
        if (fileExists('cn5g_fed_tutorials_logs.zip')) {
          archiveArtifacts artifacts: 'cn5g_fed_tutorials_logs.zip'
        }
        sh './ci-scripts/checkTutorialHtmlReport.py --job_name ' + JOB_NAME + ' --job_id ' + BUILD_ID + ' --job_url ' + BUILD_URL
        if (fileExists('test_results_oai_cn5g_tutorials.html')) {
          archiveArtifacts artifacts: 'test_results_oai_cn5g_tutorials.html'
        }
      }
    }
    cleanup {
      script {
        sh 'docker volume prune --force'
        // Removing the images that we pulled.
        for (ii = 0; ii < imageNames.size(); ii++) {
          if (pulledImages[ii]) {
            sh 'docker rmi ' + imageNames[ii] + ':' + imageTags[ii]
          }
        }
      }
    }
  }
}

def pullImageFromSelfix(idx, imageName, imageTag, branchName) {
  if ((imageTag == 'develop') && (branchName == 'develop')) {
    try {
      tag = sh returnStdout: true, script: './ci-scripts/retrieveLatestTagOnPrivateRepo.py --repo-name ' + imageName
      tag = tag.trim()
    } catch (Exception e) {
      return imageTag
    }
  } else {
    tag = imageTag
  }
  // We may have wrong image tag?
  try {
    sh 'docker pull selfix.sboai.cs.eurecom.fr/' + imageName + ':' + tag
    sh 'docker image tag selfix.sboai.cs.eurecom.fr/' + imageName + ':' + tag + ' ' + imageName + ':' + tag
    sh 'docker rmi selfix.sboai.cs.eurecom.fr/' + imageName + ':' + tag
    pulledImages[idx] = true
  } catch (Exception e) {
    echo "${imageName} Image tag to test (${imageName}:${tag} does not exist on Selfix private registry!"
    tag = imageTag
  }
  return tag
}

def checkImageInfo(imageName, origTag) {
  status = 0
  sh "echo 'Tested Tag is ${imageName}:${origTag}' > archives/${imageName}-image-info.log"
  try {
    sh "docker image inspect --format='Size = {{.Size}} bytes' ${imageName}:${origTag} >> archives/${imageName}-image-info.log"
    sh "docker image inspect --format='Date = {{.Created}}' ${imageName}:${origTag} >> archives/${imageName}-image-info.log"
  } catch (Exception e) {
    echo "${imageName} Image tag to test (${imageName}:${origTag} does not exist!"
    status = 1
  }
  return status
}

def updateDockerCompose(filename, lRanTag) {
  for (ii = 0; ii < imageNames.size(); ii++) {
    sh 'sed -i -e "s@oaisoftwarealliance/' + imageNames[ii] + ':develop@' + imageNames[ii] + ':' + imageTags[ii] + '@" ' + filename
  }
  sh 'sed -i -e "s@oaisoftwarealliance/trf-gen-cn5g:latest@trf-gen-cn5g:latest@" ' + filename
  // Using the develop variant of gnbsim
  sh 'sed -i -e "s@gnbsim:latest@gnbsim:develop@" docker-compose/docker-compose-gnbsim*.y*ml docker-compose/docker-compose*ransim.y*ml'
  // Using a dedicated version of the OAI RAN images
  sh 'sed -i -e "s@oaisoftwarealliance/oai-gnb:develop@oai-gnb:' + lRanTag + '@" docker-compose/docker-compose-slicing-ransim.yaml'
  sh 'sed -i -e "s@oaisoftwarealliance/oai-nr-ue:develop@oai-nr-ue:' + lRanTag + '@" docker-compose/docker-compose-slicing-ransim.yaml'
}

def archivesArtifacts(filename,folder) {
  sh 'cp -Rf /tmp/oai/' + folder + ' archives || true'
  // Making a copy of the docker-compose for debugging
  sh 'cp ' + filename + ' archives/' + folder + ' || true'
}

def cleanUpDockerCompose(filename) {
  sh 'git checkout -- ' + filename
  sh 'git checkout -- docker-compose/docker-compose-gnbsim*.y*ml docker-compose/docker-compose*ransim.y*ml'
  sh 'docker network prune --force'
  sh 'docker volume prune --force'
}
